/**
 * @(#)AsteroidsFrame.java
 *
 *
 * @author 
 * @version 1.00 2009/11/30
 */
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;

public class AsteroidsFrame extends JFrame implements ActionListener, KeyListener
{
	public static final int FRAME_WIDTH 	= 1000;
	public static final int[] X_MINS 		= {0,(int)((double)FRAME_WIDTH*2)/3};
	public static final int[] X_MAXS 		= {FRAME_WIDTH/3, FRAME_WIDTH};			
	public static final int FRAME_HEIGHT 	= 800;
	public static final int[] Y_MINS 		= {0,FRAME_HEIGHT*2/3};
	public static final int[] Y_MAXS 		= {(FRAME_HEIGHT)/3, FRAME_HEIGHT};
	private static final int OFFSET 		= 100;
	private static final int FRAME_X_ORIGIN	= 150;
	private static final int FRAME_Y_ORIGIN	= 250;
	private static final int FRAME_RATE = 15;
	private static final int MAX_FRAME_RATE = 30;
	private static final int START_LEVEL = 1;
	private static final int START_LIVES = 3;
	private static final int STARTING_ROCK_LIMIT = 25;
	private static final int ROCK_LIMIT = 150;	
	private static final int SUPER_SHOT_SIZE = 10;
	private static final int LEVEL_INCREMENT = 10;
	private static final int MAX_ROCK_SIZE = 200;
	public static final int SHOT_LIMIT = 200;
	private Ship shippy;
	private javax.swing.Timer shipUpdater;
	private Container guiContents;
	private ArrayList<Shot> shotList;
	private Shot[] shots;
	private ArrayList<Rock> rockList;
	private Rock[] rocks;
	private ArrayList<SuperShot> superShotList;
	private SuperShot[] superShots;
	private int level;
	private int score;
	private StatusDisplay HUD;
	private int lives;
	

    public AsteroidsFrame() 
    {
    	HUD = new StatusDisplay();
    	level = START_LEVEL;
    	score = 0;
    	lives = START_LIVES;
    	score = 0;
    	setDefaultCloseOperation(EXIT_ON_CLOSE);
    	setTitle("Asteroids");
    	setSize(FRAME_WIDTH, FRAME_HEIGHT);
//    	setPosition(FRAME_X_ORIGIN, FRAME_Y_ORIGIN);
    	setResizable(false);
    	
//    	setBackground(Color.WHITE);	//This is what causes the white flashing
    	
    	shippy = new Ship();
    	shippy.updateShip();
		shipUpdater = new javax.swing.Timer(1000/MAX_FRAME_RATE, this);
		
		
		shotList = new ArrayList<Shot>();
		rockList = new ArrayList<Rock>();
		superShotList = new ArrayList<SuperShot>();
		//		sizes = new ArrayList<int>();
		/*for(int i = 0; i<level;i++)
		{
			rockList.add(new Rock(Rock.RandomNumber(0,FRAME_WIDTH), Rock.RandomNumber(0, FRAME_HEIGHT), 50));

		}*/
		//UpdateShapes();

		
		//UpdateShapes();
		shipUpdater.start();

		addKeyListener(this);

//		DisplayLarge("Level " + level);
//		HoldScreen(3);
		shipUpdater.start();	

//		DisplayLarge("Level " + level);
//		HoldScreen(3);
		
			

    }
    
    public void paint(Graphics g)
    {
    	super.paint(g);
    	Graphics2D grapher = (Graphics2D) g;
    	grapher.draw(shippy.getShipShape());
    	if(shots == null)
    	{
    		return;
    	}
    	for(int i = 0; i<shots.length; i++)
    	{
    		grapher.draw(shots[i].getShotShape());
    	}
    	for(int i = 0; i < superShots.length; i++)
    	{
    		//System.out.println(superShots.length);
    		grapher.draw(superShots[i].getShotShape());
    	}
    	for(int i = 0; i< rocks.length; i++)
    	{
    		try
    		{
    			grapher.draw(rocks[i].getRockShape());
    		}
    		catch(NullPointerException e)
    		{
    			//rockList.remove(i);
    			
    		}
    	}
		
    }
    
    public void actionPerformed(ActionEvent event)
    {
    	if(event.getSource().equals(shipUpdater))
    	{
    		UpdateShapes();   		
    		validate();
    		update(this.getGraphics());
    	}
    }
    
    public void UpdateShapes()
    {
	   	//System.out.println("update triggered");
		shippy.updateShip();
		//shippy.printSchematics();
		shots = new Shot[shotList.size()];
		shots = shotList.toArray(shots);
		for(int i = 0; i<shots.length;i++)
		{
			shots[i].Update();
			if(shots[i].getPositionPoint().getX() >= FRAME_WIDTH || 
				shots[i].getPositionPoint().getX() <= 0 || 
				shots[i].getPositionPoint().getY() >= FRAME_HEIGHT ||
				shots[i].getPositionPoint().getY() <= 0)
			{
				shotList.remove(i);
				shots = new Shot[shotList.size()];
				shots = shotList.toArray(shots);
				i--;
			}
		}
		superShots = new SuperShot[superShotList.size()];
		superShots = superShotList.toArray(superShots);
		//System.out.println("super length = " + superShots.length);
		for(int i = 0 ; i < superShots.length ; i++)
		{
			//System.out.println("super length = " + superShots.length);
			superShots[i].Update();
			if(superShots[i].getPositionPoint().getX() >= FRAME_WIDTH || 
				superShots[i].getPositionPoint().getX() <= 0 || 
				superShots[i].getPositionPoint().getY() >= FRAME_HEIGHT ||
				superShots[i].getPositionPoint().getY() <= 0)
			{
				superShotList.remove(i);
				superShots = new SuperShot[superShotList.size()];
				superShots = superShotList.toArray(superShots);
				i--;
			}
		}
		rocks = new Rock[rockList.size()];
		rocks = rockList.toArray(rocks);
		for (int i =0; i < rocks.length ; i++)
		{
			rocks[i].Update();
			if(rocks[i].isShot(shots))
			{
				try
				{
					//rocks[i] = null;
					shotList.remove(rocks[i].whichShot(shots));
					
				}
				catch(IndexOutOfBoundsException e)
				{
					
				}
				try
				{
					score+=rocks[i].getSize();
					rockList.remove(i);
					
				}
				catch(IndexOutOfBoundsException e)
				{
					
				}
				if(rocks[i].getSize() > 30 && rockList.size() < ROCK_LIMIT)
				{
					rockList.add(rocks[i].Spawn()[0]);
					rockList.add(rocks[i].Spawn()[1]);
				}
			}
			else if(rocks[i].isShot(superShots))
			{
				if(rocks[i].isShot(superShots))
				{
					
					try
					{
						//rocks[i] = null;
						int shooter = rocks[i].whichShot(superShots);
						superShots[shooter].ShotSomething(superShotList);
						if(!superShots[shooter].IsInvincible()) 
						{
							superShotList.remove(shooter);
						}
					}
					catch(IndexOutOfBoundsException e)
					{
						
					}
					
					try
					{
						score+=rocks[i].getSize();

						rockList.remove(i);
					}
					
				
					catch(IndexOutOfBoundsException e)
					{
						
					}
					if(rocks[i].getSize() > 30 && rockList.size() < ROCK_LIMIT)
					{
						rockList.add(rocks[i].Spawn()[0]);
						rockList.add(rocks[i].Spawn()[1]);
					}
					rocks[i] = null;
					
				}
			}
		}
    	rocks = new Rock[rockList.size()];
		rocks = rockList.toArray(rocks);
		//System.out.println(shotList.size() + "\t" + rockList.size() + "\t" + superShotList.size() + "\t" + superShots.length);
		if(shippy.isHit(rocks) && !shippy.IsInvincible())
		{

			lives--;
			if(lives <= 0)
			{
				DisplayLarge("Game Over");
				HoldScreen(2);
				if(JOptionPane.showConfirmDialog(null, "Would you like to play again?", "Play Again?", JOptionPane.YES_NO_OPTION) == 1)
				{
					System.exit(0);
				}
				else
				{
					
					shippy = new Ship();
					level = START_LEVEL;
					score = 0;
					lives = START_LIVES;
					shotList = new ArrayList<Shot>();
					shots = new Shot[shotList.size()];
					shots = shotList.toArray(shots);
					rockList = new ArrayList<Rock>();
					rocks = new Rock[rockList.size()];
					rocks = rockList.toArray(rocks);
					superShotList = new ArrayList<SuperShot>();
					superShots = new SuperShot[superShotList.size()];
					superShots = superShotList.toArray(superShots);
					update(this.getGraphics());
				}
			}
			else
			{
				shippy = new Ship();
				removeKeyListener(this);
				DisplayLarge("Level " + level);
				HoldScreen(1);
				addKeyListener(this);
			}

			rockList.clear();
 
		}
    	if(rockList.isEmpty())
    	{
    		removeKeyListener(this);
    		level+= LEVEL_INCREMENT;
	    	ArrayList<Integer> sizes;
	    	sizes = new ArrayList<Integer>();
			for(int i = 0; i<level;i+=2)
			{
				if((i % 2 == 0))
				{	
					int size = 50 + i * 20;
					if (size <= MAX_ROCK_SIZE)
					{
						sizes.add(new Integer(size));
					}
					System.out.println(sizes.size() + " i = " + i + "size = " + size);
					int[] xMins = {0,700};
					int[] xMaxs = {300, 1000};
				}
				if(rockList.size() < STARTING_ROCK_LIMIT)
				{
					rockList.add(new Rock(Rock.MultiRangeRandom(X_MINS, X_MAXS), 
							Rock.MultiRangeRandom(Y_MINS, Y_MAXS), this.randomAmong(sizes.toArray(new Integer[sizes.size()]))));
				}
			}
			shotList.clear();
			shippy = new Ship();
			superShotList.clear();
		
			

			//shotList = new ArrayList<Shot>();
			//shippy = new Ship();

			DisplayLarge("Level " + level);
			HoldScreen(3);
			addKeyListener(this);
			//superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.INVINCIBLE, SuperShot.SuperType.WRAP_AROUND ,SuperShot.SuperType.PERFECT_HOMING));
			
    	}

    	//DisplaySmall("Score: " + score);
    	HUD.SetScoreDisplay(score);
    //System.gc();

    	//DisplaySmall("Score: " + score);

    }
    public void keyReleased(KeyEvent e)
    {
    	
    }
    
    public void keyTyped(KeyEvent e)
    {
    	/*
    	if(e.getKeyCode() == 32)
    	{
    		shotList.add(shippy.FireShot());
    	}
    	*/
    }
    
    public void keyPressed(KeyEvent e)
    {
    	if(e.getKeyCode() == 'P')
    	{
    		if(shipUpdater.isRunning())
    		{
    			shipUpdater.stop();
				DisplayLarge("Paused");
    			
    		}
    		else
    		{
    			shipUpdater.start();
    		}
    	}
    	else if(!shipUpdater.isRunning() && e.getKeyCode() == 'Q')
    	{
    		System.exit(0);
    	}
    	else if(shipUpdater.isRunning())
    	{	
	    	if(e.getKeyCode() == 38)
	    	{
	    		shippy.Accelerate(.6*25/FRAME_RATE,0);
	    	}
	    	if(e.getKeyCode() == 40)
	    	{
	    		shippy.Accelerate(-.6*25/FRAME_RATE,0);
	    	}
	    	if(e.getKeyCode() ==  39)
	    	{
	    		shippy.Accelerate(0,.3*25/FRAME_RATE);
	    	}
	    	if(e.getKeyCode() == 37)
	    	{
	    		shippy.Accelerate(0,-.3*25/FRAME_RATE);
	    	}
	    	
	    	if(e.getKeyCode() == 'B')
	    	{
	    		shippy.FullBrake();
	    	}
	    	if(e.getKeyCode() == 'V')
	    	{
	    		shippy.FullForward();
	    	}
	    	if(e.getKeyCode() == 'C')
	    	{
	    		shippy.FullReverse();
	    	}
	    	if(e.getKeyCode() == 'N')
	    	{
	    		shippy.Accelerate(10*25/FRAME_RATE,0);
	    	}	
	    	if(e.getKeyCode() == 'M')
	    	{
	    		shippy.Accelerate(-10*25/FRAME_RATE,0);
	    	}
	    	if((shotList.size() + superShotList.size()) <SHOT_LIMIT)
	    	{
	    		if(e.getKeyCode() == 32)
	    		{
	    			shotList.add(shippy.FireShot());
	    		}
		    	if(e.getKeyCode() == 'A')
		    	{
		   			SuperShot[] new_supers = shippy.FireSuperBurst(SuperShot.SuperType.INVINCIBLE);
		   			for(int i = 0; i < new_supers.length; i++)
		   			{
		   				superShotList.add(new_supers[i]);
		   			}
		    	}
		    	if(e.getKeyCode() == 'Q')
		    	{
		    		superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.HOMING, SuperShot.SuperType.EXPLOSIVE, SuperShot.SuperType.INVINCIBLE));
		    	}
		      	if(e.getKeyCode() == 'S')
		    	{
		    		superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.INVINCIBLE, SuperShot.SuperType.WRAP_AROUND ,SuperShot.SuperType.PERFECT_HOMING));
		   		}
		   		if(e.getKeyCode() == 'E')
		    	{
		    		superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.EXPLOSIVE));
		   		}
		   		if(e.getKeyCode() == 'W')
		    	{
		    		shippy.FireSuperBurst(superShotList, SuperShot.SuperType.CHAIN_REACTION);
		   		}
		   		if(e.getKeyCode() == 'R')
		    	{
		    		superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.CHAIN_REACTION, SuperShot.SuperType.INVINCIBLE));
		   		}
		   		if(e.getKeyCode() == 'Z')
		    	{
		    		superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.CHAIN_REACTION, SuperShot.SuperType.HOMING));
		   		}
		   		if(e.getKeyCode() == 'F')
		   		{
		   			shippy.FireSuperBurst(superShotList, SuperShot.SuperType.SELF_HOMING, SuperShot.SuperType.WRAP_AROUND, SuperShot.SuperType.EXPLOSIVE);
		   			//System.out.println("fire");
		   		}
		   		if(e.getKeyCode() == 'D')
		   		{
		   			SuperShot[] new_supers = shippy.FireSuperBurst(SuperShot.SuperType.INVINCIBLE, SuperShot.SuperType.HOMING);
		   			for(int i = 0; i < new_supers.length; i++)
		   			{
		   				superShotList.add(new_supers[i]);
		   			}
		   		}
		   		if(e.getKeyCode() == 'T')
		   		{
		   			superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.HOMING));
		   		}
		   		if(e.getKeyCode() == 'G')
		   		{
		   			superShotList.add(shippy.FireSuperShot(SuperShot.SuperType.SELF_HOMING, SuperShot.SuperType.WRAP_AROUND));
		   		}
	    	}
	   		if(e.getKeyCode() == 'X')
	   		{
	   			shotList.clear();
	   			superShotList.clear();
	   		}
	   		
    	}	
    }
    public int randomAmong(Integer[] choices)
    {
    	int choice = Rock.RandomNumber(0,choices.length - 1);
    	return choices[choice].intValue();
    }
    public void HoldScreen(int sec)
    {
    	//removeKeyListener(this);
    	long start = System.currentTimeMillis();
    	while(System.currentTimeMillis() < start + 1000 * sec)
    	{
    		//if((System.currentTimeMillis() - (start + 1000 * sec)) % FRAME_RATE == 0)
    		//{
    			//UpdateShapes();
    			//update(this.getGraphics());
    		//} 
    	}
    	//addKeyListener(this);
    }
    public void DisplayLarge(String message)
	{
    	Graphics frameGraphics = this.getGraphics();
		update(frameGraphics);
		frameGraphics.setFont(new Font("Big", Font.BOLD, 72));
		frameGraphics.drawString(message, 350, 400);
	}

	public void DisplaySmall(String message)
	{
		Graphics frameGraphics = this.getGraphics();
		//System.out.println(frameGraphics);
		update(frameGraphics);
		frameGraphics.setFont(new Font("Small", Font.BOLD, 14));
		frameGraphics.drawString(message, FRAME_WIDTH - 100,100);
	}
	
	public double GetDistance(Ship myShip, Rock tooClose)
	{
		Point2D.Double rockLocation = tooClose.GetPosition();
		Point2D.Double shipLocation = myShip.GetPosition();
		return Math.sqrt(Math.pow(rockLocation.getX() - shipLocation.getX(),2) + Math.pow(rockLocation.getY() - shipLocation.getY(),2));
		//return 3.14;
	}
	
	public Rock[] GetRocks()
	{
		return rockList.toArray(rocks);
	}
	
	public int GetTotalNumShots()
	{
		return (shotList.size() + superShotList.size());
	}
	
	public Point2D.Double GetShipPosition()
	{
		return shippy.GetPosition();
	}

} 